//Grading Template

/* ID         :  1632842
   Assignment :  TotalSize
   Class      :  CSIS 310 Data Structures
   Date       :  23 November 2011

Programming Assesment Standards

PROGRAM CORRECTNESS
   a) Functions correctly (e.g. output is correct)       (40)________
   b) Correct handling of other exceptional conditions   (10)________
      (bad input, missing files, etc.)
PROGRAM DESIGN
   c) Appropriate choice & use of data structures        (10)________
   d) Subjective "elegance" (e.g., flow, design)         (20)________
PROGRAM READABILITY AND DOCUMENTATION
   e) Header comments are complete & clear                (5)________
   f) Internal comments are used when appropriate        (15)________

TOTAL                                                   (100)________

******************************************************************/
/**
 * program TotalSize calculates the total size of all files and directories
 * in the specified directory path in bytes. 
 *
 * @author 1632842
 * @version 1
 */
import java.io.File;
import java.io.IOException;
import java.util.Deque;
import java.util.LinkedList;

public class TotalSize
{

	/**
	 * TotalSize accepts a standard command line file argument and traverses 
	 * that file structure, determining its total size in bytes.
	 * @param args
	 */
	public static void main(String[] args)
    {
        Deque<File> nodesToVisit = new LinkedList<File>();
        long runningTotal = 0;
        File visitThis = null;
        File[] listFiles;
        boolean failedSizes = false;
        String failStatement = "WARNING:  Some files may have been unreadable";
       
        // this if/else block checks the input from the command line for errors
        // if the length is correct, but the path does not exist, we get a
        // message and the program exits.  Likewise, if nothing is passed in,
        // a message is displayed and the program exits. 
        if(args.length > 0)
        { 
            visitThis = new File(args[0]);

            if(!visitThis.exists())
            {
                System.out.println("Directory does not exist in this filesystem");
                System.exit(1);
            } // if

        } // if
        else
        {
            System.out.println("You must state a filepath.");
            System.exit(1);
        } // else


        //first, push the path onto the stack
        nodesToVisit.push(visitThis);

        while(!nodesToVisit.isEmpty()) // while there are still things to visit
        {
            // pop off a File and assign it to visitThis
            visitThis = nodesToVisit.pop();
            
            // try/catch allows this length() and the listFiles attempt to not 
            // die if there is a java securitymanager in place.  In our program
            // it will toggle a boolean to add an additional print statement at
            // the end.
            try
            {
                runningTotal += visitThis.length();
                
                // grab all the children of the File we have popped off the stack
                // and visited, if any.
                listFiles = visitThis.listFiles();

            } // try
            catch(SecurityException e)
            {
                failedSizes = true;
            } // catch

            // Some files of zero length could be unreadable due to security on
            // the OS side.  I toggle the boolean for the additional clarifying
            // statement.
            if(visitThis.length() < 1)
            {

                failedSizes = true;

            } // if

            // grab all the children of the File we have popped off the stack
            // and visited, if any.
            listFiles = visitThis.listFiles();

            // listFiles() returns null if there are no children (ie, if it is
            // a file).  So, if listFiles is not null, there are children to 
            // push onto the stack, so we do.
            if(listFiles != null)
            {
                for(int i = 0; i < listFiles.length; i++)
                {
                    try
                    {
                        // this checks for any possible symlinks within the file
                        // system of a unix box.
                        if(listFiles[i].getCanonicalPath().equals(
                                    listFiles[i].getAbsolutePath()))
                        {
                            nodesToVisit.push(listFiles[i]);
                        }
                    }
                    catch(IOException e) //getCanonicalPath and getAbsolutePath
                    {                    //may throw an IOException, caught here.
                        failedSizes = true;
                    }

                }  // for

            }  // if

        }  // while
        
        // If we had some exceptions, zero file sizes, etc, we inform.
        if(failedSizes)
        {
            System.out.println(failStatement);
        }

        // Finally, we print the grand total. 
        System.out.println(runningTotal + " bytes");

    } // main
   
} // TotalSize
